package me.huanmeng.heater.socket;

import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import me.huanmeng.heater.Heater;
import me.huanmeng.heater.api.MessageListener;
import me.huanmeng.heater.api.event.PongEvent;
import me.huanmeng.heater.bot.MessageHelper;
import me.huanmeng.heater.impl.BotSocketMain;
import me.huanmeng.heater.main.HeaterMain;
import me.huanmeng.heater.runnable.HeartBeatRunnable;
import me.huanmeng.heater.signalling.Signalling;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.framing.CloseFrame;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONObject;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;

/**
 * 2021/9/26<br>
 * Heater<br>
 *
 * @author huanmeng_qwq
 */
public class BotSocketClient extends WebSocketClient implements MessageHelper {
    private static int reconnectNum;
    public static final int RECONNECT_MAX = 2;
    @Getter
    private final BotSocketMain socketMain;
    private final List<MessageListener> listeners;
    private final HeartBeatRunnable heartBeatRunnable;

    public BotSocketClient(BotSocketMain botSocketMain, URI serverUri) {
        super(serverUri);
        this.socketMain = botSocketMain;
        this.listeners = new ArrayList<>();
        this.heartBeatRunnable = new HeartBeatRunnable(this);
    }

    public void addListener(MessageListener messageListener) {
        listeners.add(messageListener);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {

    }

    @Override
    public void onMessage(String s) {
        resetReconnectNum();
        final JSONObject jsonObject = new JSONObject(s);
        if (jsonObject.has("sn")) {
            socketMain.setSn(jsonObject.getInt("sn"));
        }
        if (jsonObject.optInt("s", -1) == 1) {
            socketMain.setSession_id(UUID.fromString(jsonObject.optJSONObject("d").getString("session_id")));
        } else if (jsonObject.optInt("s", -1) == 0) {//测试
            final Signalling signalling = JSONUtil.toBean(s, Signalling.class, false);
            HeaterMain.getInstance().getSignHandler().add(signalling);
        } else if (s.equals("{\"s\":3}")) {
            Heater.callEvent(new PongEvent());
        } else {
            log(Level.WARNING, "Received an unknown message");
            debug(s);
        }
        listeners.forEach(e -> e.onMessage(new JSONObject(s)));
    }

    public void resetReconnectNum() {
        if (reconnectNum > 0) {
            reconnectNum = 0;
            log(Level.INFO, "Success Reconnect.");
        }
    }

    @SneakyThrows
    @Override
    public void onClose(int code, String reason, boolean remote) {
        debug("Socket close " + code + (remote ? " by remote" : " by client") + ": " + reason + ".");
        log(Level.WARNING, "Socket close.");
        if (code == CloseFrame.NORMAL && reason.length() > 0 || reconnectNum > RECONNECT_MAX) {
            listeners.forEach(e -> e.onClose(code, reason));
            listeners.clear();
            return;
        }
        log(Level.INFO, "Try login...");
        ++reconnectNum;
        socketMain.login();
    }

    @Override
    public void onError(Exception e) {
        e.printStackTrace();
    }
}
